package com.hup.minicontroller.common.netty.handler;

import android.util.Log;
import android.util.SparseArray;

import com.hup.minicontroller.common.netty.NettyConstant;
import com.hup.minicontroller.common.netty.NettyConstant.DataType;
import com.hup.minicontroller.common.netty.handler.DataHandler.DataMethodInvoker;
import com.hup.minicontroller.common.netty.handler.SerializerHandler.JSONSerializer;
import com.hup.minicontroller.common.netty.handler.dataListener.IDataListener;
import com.hup.minicontroller.common.netty.model.BaseDataPacket;
import com.hup.minicontroller.common.netty.model.ExceptionResponseData;
import com.hup.minicontroller.common.netty.model.UnknownData;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import lombok.Setter;

/**
 * 编解码处理器,实现对象和ByteBuf双向转换的功能;
 * 在socket收发数据时被调用,所以[基本帧数据结构/通信协议]是由本类定义的!
 * 格式:帧头int-4字节,数据类型int-4字节,数据体长度int-4字节,数据体-n字节
 * 预定义的数据类型: {@link DataType}
 *
 * @author hugan
 * @date 2021/4/25
 */
@Sharable
public class DataCodecHandler extends MessageToMessageCodec<ByteBuf, BaseDataPacket> {
    private static final String TAG = DataCodecHandler.class.getSimpleName();

    public static final DataCodecHandler INSTANCE = new DataCodecHandler();

    /**
     * 已注册的数据类型,用于 {@link #decode(ChannelHandlerContext, ByteBuf, List)}
     */
    private final SparseArray<Class<? extends BaseDataPacket>> typeMap;
    @Setter
    private SerializerHandler serializerHandler = JSONSerializer.INSTANCE;
    //private SerializerHandler serializerHandler = JavaDefaultSerializer.INSTANCE;//java的序列化处理器

    private List<IDataListener> dataListeners;

    private DataCodecHandler() {
        typeMap = new SparseArray<>();
        dataListeners = new ArrayList<>();
        registerType(ExceptionResponseData.class);
    }

    /**
     * decode解码:ByteBuf转成对象;
     * socket收到数据后被调用
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) {
        Log.d(TAG, "decode: " + ctx.channel());
        //读数据
        byteBuf.skipBytes(NettyConstant.FRAME_HEAD_FIELD_SIZE);//帧头
        int type = byteBuf.readInt();//数据类型
        int dataLength = byteBuf.readInt();//数据长度
        byte[] bytes = new byte[dataLength];//数据体
        byteBuf.readBytes(bytes);

        //反序列化
        Class<? extends BaseDataPacket> clz = typeMap.get(type);
        if (clz == null) {
            Log.w(TAG, "decode: 解析数据异常,未知类型:" + type);
            UnknownData data = new UnknownData(type, bytes.length);
            out.add(data);
            for (IDataListener dataListener : dataListeners) dataListener.onReceived(data, dataLength);
        } else {
            try {
                BaseDataPacket object = serializerHandler.deserialize(clz, bytes);
                object.setDataLength(dataLength);
                out.add(object);
                for (IDataListener dataListener : dataListeners) dataListener.onReceived(object, dataLength);
            } catch (Exception e) {
                throw new IllegalArgumentException("反序列化异常,type=" + type);
            }
        }
    }

    /**
     * encode编码:对象转ByteBuf
     * ctx.channel().writeAndFlush(obj)时被调用
     */
    @Override
    protected void encode(ChannelHandlerContext ctx, BaseDataPacket packet, List<Object> out) {
        //序列化
        int type = packet.getType();
        byte[] bytes;
        try {
            bytes = serializerHandler.serialize(packet);
        } catch (Exception e) {
            throw new IllegalArgumentException("序列化异常,type=" + type);
        }

        //写数据
        ByteBuf byteBuf = ctx.channel().alloc().ioBuffer();
        byteBuf.writeInt(NettyConstant.FRAME_HEAD);
        byteBuf.writeInt(type);
        byteBuf.writeInt(bytes.length);
        byteBuf.writeBytes(bytes);
        out.add(byteBuf);
        //用于观察不同序列化算法和数据类型时,对数据大小的影响
        for (IDataListener dataListener : dataListeners) dataListener.onSend(packet, bytes.length);
    }

    /**
     * @param dataListener {@link IDataListener}
     */
    public void addDataListener(IDataListener dataListener) {
        dataListeners.add(dataListener);
    }

    /**
     * 注册数据类型,用于解析/解码收到的数据 {@link #decode(ChannelHandlerContext, ByteBuf, List)}
     *
     * @param clz public数据类,需要继承 {@link BaseDataPacket}
     */
    public int registerType(Class<? extends BaseDataPacket> clz) {
        try {
            BaseDataPacket packet = clz.newInstance();
            int type = packet.getType();
            Class<?> old = typeMap.get(type);
            if (old != null) {
                //throw new IllegalArgumentException("类型重复:" + type);
                if (Objects.equals(old, clz)) return type;
                Log.i(TAG, "registerType: 覆盖类型:" + type + ", old=" + old + ", new=" + clz);
            }
            typeMap.put(type, clz);
            return type;
        } catch (ReflectiveOperationException e) {
            throw new IllegalArgumentException("实例化异常:" + clz, e);
        }
    }

    public void unRegisterType(int type) {
        typeMap.remove(type);
    }

    public void registerType(Map<Class<? extends BaseDataPacket>, DataMethodInvoker> map) {
        for (Map.Entry<Class<? extends BaseDataPacket>, DataMethodInvoker> entry : map.entrySet()) {
            Class<? extends BaseDataPacket> aClass = entry.getKey();
            DataMethodInvoker dataMethodInvoker = entry.getValue();
            typeMap.put(dataMethodInvoker.getDataType(), aClass);
        }
    }

}
